/****************************************************************************/
/* File: scs-controller.cpp                                                 */
/* Desc: Implementation file for the input game controller type             */
/* Created: 2014-11-16                                                      */
/* Revised: 2014-11-22                                                      */
/* Authors: Sterling P. de Mille                                            */
/****************************************************************************/

// specification file
#include "scs-controller.h"

SCScontroller* SCScontroller::m_pkSingleton = NULL;

SCScontroller::SCScontroller ()
{
	assert(!m_pkSingleton);
	m_pkSingleton = this;

	m_pkController = NULL;
	SDL_InitSubSystem(SDL_INIT_JOYSTICK);
	SDL_JoystickEventState(SDL_ENABLE);
	ActivateController();
	m_fDeadZone = 0.25f;
}

SCScontroller::~SCScontroller ()
{
	if (SDL_JoystickOpened(0)) SDL_JoystickClose(m_pkController);
	m_iControllerActive = SCS_FALSE;
	m_iControllerConnected = SCS_FALSE;
	m_pkController = NULL;
	m_fDeadZone = 0;

	assert(m_pkSingleton);
	m_pkSingleton = NULL;
}

void SCScontroller::ActivateController ()
{
	if (m_pkController) {
		m_iControllerActive = SCS_FALSE;
		SDL_JoystickClose(m_pkController);
		m_pkController = NULL;
	}
	if (SDL_WasInit(SDL_INIT_JOYSTICK)) {
		SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
	}
	SDL_InitSubSystem(SDL_INIT_JOYSTICK);
	if (SDL_NumJoysticks()) {
		m_pkController = SDL_JoystickOpen(0);
	}
	m_iControllerActive = ((m_pkController != NULL) && SDL_JoystickOpened(0));
	if (m_iControllerActive) {
		m_iControllerConnected = (SDL_JoystickName(0) != NULL);
		ClearState();
	}
}

SCSbool SCScontroller::ControllerActive ()
{
	return m_iControllerActive;
}

SCSbool SCScontroller::ControllerConnected ()
{
	return m_iControllerConnected;
}

SCScontrolmap SCScontroller::GamepadMap () const
{
	SCScontrolmap kMap;
	for (SCSint iButton = 0; iButton < 32; iButton++) {
		kMap.aiButtons[iButton] = iButton;
	}
	for (SCSint iAxis = 0; iAxis < 6; iAxis++) {
		kMap.aiAxes[iAxis] = iAxis;
	}
	for (SCSint iDirection = 0; iDirection < 6; iDirection++) {
		kMap.aiDpad[iDirection] = iDirection;
	}
	return kMap;
}

SCScontrolmap SCScontroller::XboxMap () const
{
	SCScontrolmap kMap;
	for (SCSint iButton = 0; iButton < 32; iButton++) {
		kMap.aiButtons[iButton] = iButton;
	}
	for (SCSint iAxis = 0; iAxis < 6; iAxis++) {
		kMap.aiAxes[iAxis] = iAxis;
	}
	for (SCSint iDirection = 0; iDirection < 6; iDirection++) {
		kMap.aiDpad[iDirection] = iDirection;
	}
	kMap.aiAxes[AXIS_THUMBSTICK_LEFT_X] = 0;
	kMap.aiAxes[AXIS_THUMBSTICK_LEFT_Y] = 1;
	kMap.aiAxes[AXIS_TRIGGER_LEFT] = 2;
	kMap.aiAxes[AXIS_THUMBSTICK_RIGHT_X] = 3;
	kMap.aiAxes[AXIS_THUMBSTICK_RIGHT_Y] = 4;
	kMap.aiAxes[AXIS_TRIGGER_RIGHT] = 5;

	kMap.aiButtons[BUTTON_FACE_TOP] = 3;
	kMap.aiButtons[BUTTON_FACE_BOTTOM] = 0;
	kMap.aiButtons[BUTTON_FACE_LEFT] = 2;
	kMap.aiButtons[BUTTON_FACE_RIGHT] = 1;
	kMap.aiButtons[BUTTON_SHOULDER_LEFT] = 4;
	kMap.aiButtons[BUTTON_SHOULDER_RIGHT] = 5;
	kMap.aiButtons[BUTTON_CONTROL_LEFT] = 6;
	kMap.aiButtons[BUTTON_CONTROL_RIGHT] = 7;
	kMap.aiButtons[BUTTON_CONTROL_CENTER] = 8;
	kMap.aiButtons[BUTTON_THUMBSTICK_LEFT] = 9;
	kMap.aiButtons[BUTTON_THUMBSTICK_RIGHT] = 10;

	return kMap;
}

void SCScontroller::SetDeadZone (SCSfloat fDeadZone)
{
	m_fDeadZone = fDeadZone;
}

SCSfloat SCScontroller::GetDeadZone ()
{
	return m_fDeadZone;
}

SCSbool SCScontroller::ButtonPress (SCSint iButton)
{
	SCSint32u iFlag = (0x1 << (iButton & 0x1f));
	return ((m_kCurrentState.iButtonStates & iFlag) && !(m_kPreviousState.iButtonStates & iFlag));
}

SCSbool SCScontroller::ButtonRelease (SCSint iButton)
{
	SCSint32u iFlag = (0x1 << (iButton & 0x1f));
	return (!(m_kCurrentState.iButtonStates & iFlag) && (m_kPreviousState.iButtonStates & iFlag));
}

SCSbool SCScontroller::ButtonPressed (SCSint iButton)
{
	SCSint32u iFlag = (0x1 << (iButton & 0x1f));
	return (m_kCurrentState.iButtonStates & iFlag);
}

SCSbool SCScontroller::ButtonReleased (SCSint iButton)
{
	SCSint32u iFlag = (0x1 << (iButton & 0x1f));
	return !(m_kCurrentState.iButtonStates & iFlag);
}

SCSfloat SCScontroller::AxisPosition (SCSint iAxis)
{
	SCSfloat fValue = 0;
	SCSfloat fScale = 0x7fff;
	switch(iAxis) {
	case AXIS_THUMBSTICK_LEFT_X:
		fValue = m_kCurrentState.iLeftThumbstickX;
		break;
	case AXIS_THUMBSTICK_LEFT_Y:
		fValue = m_kCurrentState.iLeftThumbstickY;
		break;
	case AXIS_THUMBSTICK_RIGHT_X:
		fValue = m_kCurrentState.iRightThumbstickX;
		break;
	case AXIS_THUMBSTICK_RIGHT_Y:
		fValue = m_kCurrentState.iRightThumbstickY;
		break;
	case AXIS_TRIGGER_LEFT:
		fValue = m_kCurrentState.iLeftTrigger;
		break;
	case AXIS_TRIGGER_RIGHT:
		fValue = m_kCurrentState.iRightTrigger;
		break;
	}
	if (fValue < 0) fScale++;
	return (fValue / fScale);
}

SCSfloat SCScontroller::AxisMotion (SCSint iAxis)
{
	SCSfloat fDelta;
	SCSfloat fScale = 0x7fff;
	switch(iAxis) {
	case AXIS_THUMBSTICK_LEFT_X:
		fDelta = (m_kCurrentState.iLeftThumbstickX - m_kPreviousState.iLeftThumbstickX);
		break;
	case AXIS_THUMBSTICK_LEFT_Y:
		fDelta = (m_kCurrentState.iLeftThumbstickY - m_kPreviousState.iLeftThumbstickY);
		break;
	case AXIS_THUMBSTICK_RIGHT_X:
		fDelta = (m_kCurrentState.iRightThumbstickX - m_kPreviousState.iRightThumbstickX);
		break;
	case AXIS_THUMBSTICK_RIGHT_Y:
		fDelta = (m_kCurrentState.iRightThumbstickY - m_kPreviousState.iRightThumbstickY);
		break;
	case AXIS_TRIGGER_LEFT:
		fDelta = (m_kCurrentState.iLeftTrigger - m_kPreviousState.iLeftTrigger);
		break;
	case AXIS_TRIGGER_RIGHT:
		fDelta = (m_kCurrentState.iRightTrigger - m_kPreviousState.iRightTrigger);
		break;
	}
	if (fDelta < 0) fScale++;
	return (fDelta / fScale);
}

SCSint SCScontroller::AxisPress (SCSint iAxis)
{
	SCSfloat fCurrentValue;
	SCSfloat fPreviousValue;
	SCSfloat fCurrentScale = 0x7fff;
	SCSfloat fPreviousScale = 0x7fff;

	switch(iAxis) {
	case AXIS_THUMBSTICK_LEFT_X:
		fCurrentValue = m_kCurrentState.iLeftThumbstickX;
		fPreviousValue = m_kPreviousState.iLeftThumbstickX;
		break;
	case AXIS_THUMBSTICK_LEFT_Y:
		fCurrentValue = m_kCurrentState.iLeftThumbstickY;
		fPreviousValue = m_kPreviousState.iLeftThumbstickY;
		break;
	case AXIS_THUMBSTICK_RIGHT_X:
		fCurrentValue = m_kCurrentState.iRightThumbstickX;
		fPreviousValue = m_kPreviousState.iRightThumbstickX;
		break;
	case AXIS_THUMBSTICK_RIGHT_Y:
		fCurrentValue = m_kCurrentState.iRightThumbstickY;
		fPreviousValue = m_kPreviousState.iRightThumbstickY;
		break;
	}
	if (fCurrentValue < 0) fCurrentScale++;
	if (fPreviousValue < 0) fPreviousScale++;
	fCurrentValue = (fCurrentValue / fCurrentScale);
	fPreviousValue = (fPreviousValue / fPreviousScale);
	if (fCurrentValue < 0) {
		if ((fPreviousValue >= -m_fDeadZone) && (-m_fDeadZone > fCurrentValue)) {
			return -1;
		}
	} else {
		if ((fPreviousValue <= m_fDeadZone) && (m_fDeadZone < fCurrentValue)) {
			return 1;
		}
	}
	return 0;
}

SCSbool SCScontroller::AxisRelease (SCSint iAxis)
{
	return (AxisPress(iAxis) == 0);
}

SCSint SCScontroller::AxisPressed (SCSint iAxis)
{
	SCSfloat fCurrentValue;
	SCSfloat fCurrentScale = 0x7fff;
	switch(iAxis) {
	case AXIS_THUMBSTICK_LEFT_X:
		fCurrentValue = m_kCurrentState.iLeftThumbstickX;
		break;
	case AXIS_THUMBSTICK_LEFT_Y:
		fCurrentValue = m_kCurrentState.iLeftThumbstickY;
		break;
	case AXIS_THUMBSTICK_RIGHT_X:
		fCurrentValue = m_kCurrentState.iRightThumbstickX;
		break;
	case AXIS_THUMBSTICK_RIGHT_Y:
		fCurrentValue = m_kCurrentState.iRightThumbstickY;
		break;
	}
	if (fCurrentValue < 0) fCurrentScale++;
	fCurrentValue = (fCurrentValue / fCurrentScale);
	if (fCurrentValue < 0) {
		if (-m_fDeadZone > fCurrentValue) {
			return -1;
		}
	} else {
		if (m_fDeadZone < fCurrentValue) {
			return 1;
		}
	}
	return 0;
}

SCSbool SCScontroller::AxisReleased (SCSint iAxis)
{
	return (AxisPressed(iAxis) == 0);
}

SCSbool SCScontroller::DpadPress (SCSint iDpadDirection)
{
	SCSint32u iFlags = iDpadDirection;
	return (((m_kCurrentState.iDpadState) == iFlags) && !(m_kPreviousState.iDpadState == iFlags));
}

SCSbool SCScontroller::DpadRelease (SCSint iDpadDirection)
{
	SCSint32u iFlags = iDpadDirection;
	return (!(m_kCurrentState.iDpadState == iFlags) && (m_kPreviousState.iDpadState == iFlags));
}

SCSbool SCScontroller::DpadPressed (SCSint iDpadDirection)
{
	SCSint32u iFlags = iDpadDirection;
	return (m_kPreviousState.iDpadState == iFlags);
}

SCSbool SCScontroller::DpadReleased (SCSint iDpadDirection)
{
	SCSint32u iFlags = iDpadDirection;
	return !(m_kPreviousState.iDpadState == iFlags);
}

void SCScontroller::ClearState ()
{
	m_kCurrentState.iButtonStates = m_kPreviousState.iButtonStates = 0;
	m_kCurrentState.iDpadState = m_kPreviousState.iDpadState = 0;
	m_kCurrentState.iLeftThumbstickX = m_kPreviousState.iLeftThumbstickX = 0;
	m_kCurrentState.iLeftThumbstickY = m_kPreviousState.iLeftThumbstickY = 0;
	m_kCurrentState.iRightThumbstickX = m_kPreviousState.iRightThumbstickX = 0;
	m_kCurrentState.iRightThumbstickY = m_kPreviousState.iRightThumbstickY = 0;
	m_kCurrentState.iLeftTrigger = m_kPreviousState.iLeftTrigger = 0;
	m_kCurrentState.iRightTrigger = m_kPreviousState.iRightTrigger = 0;
}

void SCScontroller::Update ()
{
	SCSint32u iDpadState;
	SCSint32u iButtonStates;
	SCSint iButtonCount;

	SDL_JoystickUpdate();
	m_iControllerConnected = (SDL_JoystickName(0) != NULL);
	if (m_iControllerConnected && m_iControllerActive) {
		memcpy(&m_kPreviousState, &m_kCurrentState, sizeof(SCScontrolstate));
		m_kCurrentState.iLeftThumbstickX = SDL_JoystickGetAxis(m_pkController, AXIS_THUMBSTICK_LEFT_X);
		m_kCurrentState.iLeftThumbstickY = SDL_JoystickGetAxis(m_pkController, AXIS_THUMBSTICK_LEFT_Y);
		m_kCurrentState.iRightThumbstickX = SDL_JoystickGetAxis(m_pkController, AXIS_THUMBSTICK_RIGHT_X);
		m_kCurrentState.iRightThumbstickY = SDL_JoystickGetAxis(m_pkController, AXIS_THUMBSTICK_RIGHT_Y);
		m_kCurrentState.iLeftTrigger = SDL_JoystickGetAxis(m_pkController, AXIS_TRIGGER_LEFT);
		m_kCurrentState.iRightTrigger = SDL_JoystickGetAxis(m_pkController, AXIS_TRIGGER_RIGHT);

		iButtonStates = 0;
		iButtonCount = ButtonCount();
		if (iButtonCount > 32) iButtonCount = 32;
		for (SCSint iButton = 0; iButton < iButtonCount; iButton++) {
			if (SDL_JoystickGetButton(m_pkController, iButton)) iButtonStates |= (0x1 << iButton);
		}
		m_kCurrentState.iButtonStates = iButtonStates;

		iDpadState = 0;
		if (SDL_JoystickGetHat(m_pkController, 0) & SDL_HAT_UP) iDpadState |= DPAD_UP;
		if (SDL_JoystickGetHat(m_pkController, 0) & SDL_HAT_DOWN) iDpadState |= DPAD_DOWN;
		if (SDL_JoystickGetHat(m_pkController, 0) & SDL_HAT_LEFT) iDpadState |= DPAD_LEFT;
		if (SDL_JoystickGetHat(m_pkController, 0) & SDL_HAT_RIGHT) iDpadState |= DPAD_RIGHT;
		m_kCurrentState.iDpadState = iDpadState;
	}
}

SCSint SCScontroller::ButtonCount ()
{
	return SDL_JoystickNumButtons(m_pkController);
}

SCSint SCScontroller::AxisCount ()
{
	return SDL_JoystickNumAxes(m_pkController);
}

SCSint SCScontroller::DpadCount ()
{
	return SDL_JoystickNumHats(m_pkController);
}

SCSbool SCScontroller::AtariCompatible ()
{
	if (ButtonCount() < 1) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::NintendoCompatible ()
{
	if (ButtonCount() < 4) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::SegaCompatible ()
{
	if (ButtonCount() < 4) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::SegaGenesisCompatible ()
{
	if (ButtonCount() < 4) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::SuperNintendoCompatible ()
{
	if (ButtonCount() < 8) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::SegaSaturnCompatible ()
{
	if (ButtonCount() < 9) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::Nintendo64Compatible ()
{
	if (ButtonCount() < 10) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;
	if (AxisCount() < 2) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::SegaDreamcastCompatible ()
{
	if (ButtonCount() < 5) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;
	if (AxisCount() < 4) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::ArcadepadCompatible ()
{
	if (ButtonCount() < 10) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::GamepadCompatible ()
{
	if (ButtonCount() < 10) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;
	if (AxisCount() < 6) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::XboxCompatible ()
{
	if (ButtonCount() < 11) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;
	if (AxisCount() < 6) return SCS_FALSE;

	return SCS_TRUE;
}

SCSbool SCScontroller::PlaystationCompatible ()
{
	if (ButtonCount() < 10) return SCS_FALSE;
	if (DpadCount() < 1) return SCS_FALSE;
	if (AxisCount() < 6) return SCS_FALSE;

	return SCS_TRUE;
}

SCScontroller* SCScontroller::Singleton ()
{
	return m_pkSingleton;
}
